﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ZLGCANoptimize
{
    public class ZLGCANHelper
    {
        #region 委托
        public Action<string> Logger = null;
        #endregion

        /* CAN FD payload length and DLC definitions according to ISO 11898-7 */
        const int CANFD_MAX_DLC = 15;
        const int CANFD_MAX_DLEN = 64;

        /* CAN payload length and DLC definitions according to ISO 11898-1 */
        const int CAN_MAX_DLC = 8;
        const int CAN_MAX_DLEN = 8;

        public ZLGCANHelper(DeviceType deviceType)
        {
            _deviceType = (uint)deviceType;
            Console.WriteLine(_deviceType);
        }
        public ZLGCANHelper(IntPtr device_handle)
        {
            this._device_handle = device_handle;
            if ((int)device_handle == 0)
            {
                open = false;
            }
            else
            {
                open = true;
            }
        }
        public ZLGCANHelper canFactory { get; set; }
        public ZLGCANHelper(string ip, int port, int channel, ZLGCANHelper canFactory)
        {
            this.RemoteAddr = ip;
            this.RemotePort = port;
            this.channel = channel;
            this.canFactory = canFactory;
        }

        IntPtr _device_handle;//设备句柄
        IntPtr[] intPtrs = new IntPtr[8];
        uint _deviceType;//设备类型
        bool open;//设备打开状态

        /// <summary>
        /// 通道
        /// </summary>
        private int channel { get; set; }
        public NetMode Net_Mode { get; set; }//
        public int LocalPort { get; set; }//本地端口

        /// <summary>
        /// CAN盒IP地址
        /// </summary>
        public string RemoteAddr { get; set; }

        /// <summary>
        /// CAN盒Port
        /// </summary>
        public int RemotePort { get; set; }

        public Standard standard { get; set; }

        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="deviceType">参数一，设备类型</param>
        /// <param name="device_index">参数二，设备索引号</param>
        /// <param name="reserved"></param>
        public bool Open(uint device_index, uint reserved = 0)
        {

            _device_handle = Method.ZCAN_OpenDevice(_deviceType, device_index, reserved);
            if (null == (int)_device_handle)
            {
                Logger?.Invoke("打开设备失败,请检查设备类型和设备索引号是否正确 ");
                return false;
            }
            open = true;
            Logger?.Invoke("打开设备成功");
            return open;
        }
        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="deviceType"></param>
        /// <param name="device_index"></param>
        /// <param name="reserved"></param>
        public IntPtr Opens(uint device_index, uint reserved = 0)
        {

            _device_handle = Method.ZCAN_OpenDevice(_deviceType, device_index, reserved);
            if (null == (int)_device_handle)
            {
                //MessageBox.Show("打开设备失败,请检查设备类型和设备索引号是否正确", "提示",
                //        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Logger?.Invoke("打开设备失败,请检查设备类型和设备索引号是否正确 ");
                return _device_handle;
            }
            open = true;
            Logger?.Invoke("打开设备成功");
            return _device_handle;
        }
        public IntPtr Init(int channel)
        {
            IntPtr channel_handle_ = new IntPtr();
            if (!open)
            {
                //  MessageBox.Show("设备还没打开", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Logger?.Invoke("设备还没打开");
                return channel_handle_;
            }

            bool netDevice = _deviceType == Define.ZCAN_CANETTCP || _deviceType == Define.ZCAN_CANETUDP ||
                _deviceType == Define.ZCAN_CANFDNET_400U_TCP || _deviceType == Define.ZCAN_CANFDNET_400U_UDP ||
                _deviceType == Define.ZCAN_CANFDNET_200U_TCP || _deviceType == Define.ZCAN_CANFDNET_200U_UDP || _deviceType == Define.ZCAN_CANFDNET_800U_TCP ||
                _deviceType == Define.ZCAN_CANFDNET_800U_UDP;
            bool canfdnetDevice = _deviceType == Define.ZCAN_CANFDNET_400U_TCP || _deviceType == Define.ZCAN_CANFDNET_400U_UDP ||
                _deviceType == Define.ZCAN_CANFDNET_200U_TCP || _deviceType == Define.ZCAN_CANFDNET_200U_UDP || _deviceType == Define.ZCAN_CANFDNET_800U_TCP ||
                _deviceType == Define.ZCAN_CANFDNET_800U_UDP;
            bool pcieCanfd = _deviceType == Define.ZCAN_PCIECANFD_100U ||
                _deviceType == Define.ZCAN_PCIECANFD_200U ||
                _deviceType == Define.ZCAN_PCIECANFD_400U ||
                _deviceType == Define.ZCAN_PCIECANFD_200U_EX;
            bool usbCanfd = _deviceType == Define.ZCAN_USBCANFD_100U ||
                _deviceType == Define.ZCAN_USBCANFD_200U ||
                _deviceType == Define.ZCAN_USBCANFD_MINI;
            bool canfdDevice = usbCanfd || pcieCanfd;


            if (netDevice)
            {
                bool tcpDevice = _deviceType == Define.ZCAN_CANETTCP || _deviceType == Define.ZCAN_CANFDNET_400U_TCP || _deviceType == Define.ZCAN_CANFDNET_200U_TCP || _deviceType == Define.ZCAN_CANFDNET_800U_TCP;
                bool server = tcpDevice && Net_Mode == 0;
                if (tcpDevice)
                {
                    setNetMode(channel);
                    if (server)
                    {
                        setLocalPort(channel);
                    }
                    else
                    {
                        setRemoteAddr(channel);
                        setRemotePort(channel);
                    }
                }
                else
                {
                    setLocalPort(channel);
                    setRemoteAddr(channel);
                    setRemotePort(channel);
                }
            }
            else
            {
                if (usbCanfd)
                {
                    if (!setCANFDStandard(channel)) //设置CANFD标准
                    {
                        Logger?.Invoke("设置CANFD标准失败");
                        //  MessageBox.Show("设置CANFD标准失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        return channel_handle_;
                    }
                }
                //if (checkBox_ABIT.Checked)//设置波特率
                //{
                //    if (!setCustomBaudrate())
                //    {
                //        MessageBox.Show("设置自定义波特率失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                //        return;
                //    }
                //}
                else
                {
                    //if (!canfdDevice)
                    //{
                    //    if (!setBaudrate(kBaudrate[comboBox_baud.SelectedIndex]))
                    //    {
                    //        MessageBox.Show("设置波特率失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    //        return;
                    //    }
                    //}
                    //else
                    //{
                    //    bool result = true;
                    //    if (usbCanfd)
                    //    {
                    //        if (type == Define.ZCAN_USBCANFD_200U)
                    //        {
                    //            string path = "0/set_device_recv_merge";
                    //            string value = "0";
                    //            Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(value));
                    //        }
                    //        result = setFdBaudrate(kUSBCANFDAbit[comboBox_ABIT.SelectedIndex], kUSBCANFDDbit[comboBox_DBIT.SelectedIndex]);
                    //    }
                    //    else if (pcieCanfd)
                    //    {
                    //        result = setFdBaudrate(kPCIECANFDAbit[comboBox_ABIT.SelectedIndex], kPCIECANFDDbit[comboBox_DBIT.SelectedIndex]);
                    //        if (type == Define.ZCAN_PCIECANFD_400U || type == Define.ZCAN_PCIECANFD_200U_EX)
                    //        {
                    //            string path = "0/set_device_recv_merge";
                    //            string value = "0";
                    //            Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(value));
                    //        }
                    //    }
                    //    if (!result)
                    //    {
                    //        MessageBox.Show("设置波特率失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    //        return;
                    //    }
                    //}
                }
            }
            ZCAN_CHANNEL_INIT_CONFIG config_ = new ZCAN_CHANNEL_INIT_CONFIG();
            #region 
            //if (!m_bCloud && !netDevice)
            //{
            //    config_.canfd.mode = (byte)comboBox_mode.SelectedIndex;
            //    if (usbCanfd)
            //    {
            //        config_.can_type = Define.TYPE_CANFD;
            //    }
            //    else if (pcieCanfd)
            //    {
            //        config_.can_type = Define.TYPE_CANFD;
            //        config_.can.filter = 0;
            //        config_.can.acc_code = 0;
            //        config_.can.acc_mask = 0xFFFFFFFF;
            //        config_.can.mode = 0;
            //    }
            //    else
            //    {
            //        config_.can_type = Define.TYPE_CAN;
            //        config_.can.filter = 0;
            //        config_.can.acc_code = 0;
            //        config_.can.acc_mask = 0xFFFFFFFF;
            //        config_.can.mode = 0;
            //    }
            //}
            #endregion
            IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config_));
            Marshal.StructureToPtr(config_, pConfig, true);

            //int size = sizeof(ZCAN_CHANNEL_INIT_CONFIG);
            //IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);
            //System.Runtime.InteropServices.Marshal.StructureToPtr(config_, ptr, true);
            channel_handle_ = Method.ZCAN_InitCAN(_device_handle, (uint)channel, pConfig);
            Marshal.FreeHGlobal(pConfig);

            //Marshal.FreeHGlobal(ptr);

            if ((int)channel_handle_ <= 0)
            {
                Logger?.Invoke("初始化CAN失败");
                //   MessageBox.Show("初始化CAN失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return channel_handle_;
            }

            //if (!m_bCloud && !netDevice)
            //{
            //    if (usbCanfd && checkBox_resistance.Checked)
            //    {
            //        if (!setResistanceEnable())
            //        {
            //            MessageBox.Show("使能终端电阻失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            //            return;
            //        }
            //    }

            //    if (usbCanfd && !setFilter())
            //    {
            //        MessageBox.Show("设置滤波失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            //        return;
            //    }
            //    if (type == Define.ZCAN_USBCAN_2E_U || type == Define.ZCAN_USBCAN_E_U)
            //    {
            //        if (!setFilter())
            //        {
            //            MessageBox.Show("设置滤波失败", "提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            //            return;
            //        }

            //    }

            //}
            intPtrs[channel] = channel_handle_;
            Logger?.Invoke("初始化CAN成功");
            return channel_handle_;
        }

        public bool Start(IntPtr channel_handle_)
        {
            if (Method.ZCAN_StartCAN(channel_handle_) != Define.STATUS_OK)
            {
                Logger?.Invoke("启动CAN失败");
                Console.WriteLine("启动CAN失败");
                return false;
            }

            return true;

        }

        public bool Reset(IntPtr channel_handle_)
        {
            string path = channel_handle_ + "/clear_auto_send";
            string value = "0";
            uint result = Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(value));
            if (Method.ZCAN_ResetCAN(channel_handle_) != Define.STATUS_OK)
            {
                Logger?.Invoke("复位失败");
                Console.WriteLine("复位失败");
                return true;
            }

            return true;
        }

        public uint Close()
        {
            uint re = Method.ZCAN_CloseDevice(_device_handle);
            if (re == 1)
            {
                Logger?.Invoke("关闭设备成功");
            }
            else
            {
                Logger?.Invoke("关闭设备失败");
            }

            return re;

        }
        public bool SentData(int channel, uint id, string data, bool canfd_exp = false, SendType sendType = SendType.正常发送, Protocol protocol = Protocol.CAN, Frametype frametype = Frametype.标准帧, int CANFD_BRS = 0x01)
        {
            int canfd_exp_index = 0;
            if (canfd_exp)
            {
                canfd_exp_index = 1;
            }

            int frame_type_index = ((int)frametype);
            int protocol_index = ((int)protocol);
            int send_type_index = ((int)sendType);

            uint result; //发送的帧数

            if (0 == protocol_index) //can
            {

                ZCAN_Transmit_Data can_data = new ZCAN_Transmit_Data();
                can_data.frame.can_id = MakeCanId(id, frame_type_index, 0, 0);
                can_data.frame.data = new byte[8];
                can_data.frame.can_dlc = (byte)SplitData(data, ref can_data.frame.data, CAN_MAX_DLEN);
                can_data.transmit_type = (uint)send_type_index;
                IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(can_data));
                Marshal.StructureToPtr(can_data, ptr, true);
                result = Method.ZCAN_Transmit(intPtrs[channel], ptr, 1);
                Marshal.FreeHGlobal(ptr);
            }
            else //canfd
            {

                ZCAN_TransmitFD_Data canfd_data = new ZCAN_TransmitFD_Data();
                canfd_data.frame.can_id = MakeCanId(id, frame_type_index, 0, 0);
                canfd_data.frame.data = new byte[64];
                canfd_data.frame.len = (byte)SplitData(data, ref canfd_data.frame.data, CANFD_MAX_DLEN);
                canfd_data.transmit_type = (uint)send_type_index;
                canfd_data.frame.flags = (byte)((canfd_exp_index != 0) ? CANFD_BRS : 0);
                IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(canfd_data));
                Marshal.StructureToPtr(canfd_data, ptr, true);
                result = Method.ZCAN_TransmitFD(intPtrs[channel], ptr, 1);
                Marshal.FreeHGlobal(ptr);
            }

            if (result != 1)
            {
                Logger?.Invoke("发送数据失败");

                Console.WriteLine("发送数据失败");
                AddErr(intPtrs[channel]);
                return false;
            }
            else
            {
                Logger?.Invoke("发送数据成功");
                Console.WriteLine("发送数据成功");
                return true;
            }
        }

        const int TYPE_CAN = 0;
        const int TYPE_CANFD = 1;
        static object locker = new object();
        /// <summary>
        /// 数据接收函数
        /// </summary>
        /// <param name="channel">通道</param>
        /// <param name="id">指令ID</param>
        /// <param name="fps">
        /// 接收指令响应相同结果总次数（如果为1，那么应该是接收到结果，就直接返回，当前使用传递的都是3，所以都只会通过超时之后跳出此方法）
        /// </param>
        /// <param name="timeout">
        /// 等待响应超时时间（如果需要提高指令响应速度，可以适当将这个时间改短一点）
        /// </param>
        /// <returns></returns>
        public List<ZCAN_Receive_Data> RecvData(int channel, uint id, int fps, double timeout = 1000)
        {

            List<ZCAN_Receive_Data> can_data = new List<ZCAN_Receive_Data>();
            uint len;
            int _fps = 0;
            var start = DateTime.Now;
            while (true)
            {
                lock (locker)
                {

                    len = Method.ZCAN_GetReceiveNum(intPtrs[channel], TYPE_CAN);
                    if (len > 0)
                    {
                        int size = Marshal.SizeOf(typeof(ZCAN_Receive_Data));
                        IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
                        len = Method.ZCAN_Receive(intPtrs[channel], ptr, len, 50);
                        for (int i = 0; i < len; ++i)
                        {
                            ZCAN_Receive_Data zCAN_Receive_Data = (ZCAN_Receive_Data)Marshal.PtrToStructure(
                                  (IntPtr)((Int64)ptr + i * size), typeof(ZCAN_Receive_Data));
                            if (zCAN_Receive_Data.frame.can_id == id)
                            {
                                can_data.Add(zCAN_Receive_Data);
                                _fps++;
                            }
                            else if (id == 0)
                            {
                                can_data.Add(zCAN_Receive_Data);
                                _fps++;
                            }
                        }
                        // OnRecvCANDataEvent(can_data, len);
                        Marshal.FreeHGlobal(ptr);
                    }
                }
                if (_fps >= fps)
                {
                    //备注
                    //fps理解为，接收3次数据通道中的数据
                    //这里如果将fps更改为1，接收响应的速度应该会快很多，当前传递的是3，得到的结果永远都是 _fps < fps，所以不会出现响应了就跳出的操作，2024-11-21
                    break;  
                }
                var stop = DateTime.Now;
                double haos = (stop - start).TotalMilliseconds;
                if (haos >= timeout)
                {
                    //Logger?.Invoke("接收CAN数据超时");
                    break;
                }
                Thread.Sleep(10);
            }
            return can_data;
        }

        public ZCAN_ReceiveFD_Data[] RecvFDData(int channel, uint id, int fps, double timeout = 2000)
        {
            ZCAN_ReceiveFD_Data[] canfd_data = new ZCAN_ReceiveFD_Data[10000];
            uint len;
            int _fps = 0;
            var start = DateTime.Now;
            while (true)
            {
                lock (locker)
                {

                    len = Method.ZCAN_GetReceiveNum(intPtrs[channel], TYPE_CANFD);
                    if (len > 0)
                    {
                        int size = Marshal.SizeOf(typeof(ZCAN_ReceiveFD_Data));
                        IntPtr ptr = Marshal.AllocHGlobal((int)len * size);
                        len = Method.ZCAN_ReceiveFD(intPtrs[channel], ptr, len, 50);
                        for (int i = 0; i < len; ++i)
                        {
                            ZCAN_ReceiveFD_Data zCAN_ReceiveFD_Data = (ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(
                               (IntPtr)((Int64)ptr + i * size), typeof(ZCAN_ReceiveFD_Data));

                            if (ZLGCanTool.GetId(zCAN_ReceiveFD_Data.frame.can_id) == id)
                            {
                                canfd_data[_fps] = zCAN_ReceiveFD_Data;
                                _fps++;
                            }
                        }
                        //   OnRecvFDDataEvent(canfd_data, len);

                        Marshal.FreeHGlobal(ptr);
                    }

                }
                if (_fps >= fps)
                {
                    break;
                }
                var stop = DateTime.Now;
                double haos = (stop - start).TotalMilliseconds;
                if (haos >= timeout)
                {
                    Logger?.Invoke("接收CANFD数据超时");
                    break;
                }
                Thread.Sleep(10);
            }

            return canfd_data;
        }
        private void AddErr(IntPtr channel_handle_)
        {
            ZCAN_CHANNEL_ERROR_INFO pErrInfo = new ZCAN_CHANNEL_ERROR_INFO();
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(pErrInfo));
            Marshal.StructureToPtr(pErrInfo, ptr, true);
            if (Method.ZCAN_ReadChannelErrInfo(channel_handle_, ptr) != Define.STATUS_OK)
            {
                Logger?.Invoke("获取错误信息失败");
                Console.WriteLine("获取错误信息失败");
            }
            Marshal.FreeHGlobal(ptr);

            string errorInfo = String.Format("错误码：{0:D1}", pErrInfo.error_code);
            Console.WriteLine(errorInfo);
            Logger?.Invoke(errorInfo);
            //  int index = listBox.Items.Add(errorInfo);
            //listBox.SelectedIndex = index;
        }

        //拆分text到发送data数组
        private int SplitData(string data, ref byte[] transData, int maxLen)
        {
            try
            {
                //10 13 2E F1 8C 20 32  33
                string[] dataArray = data.Split(' ');
                for (int i = 0; (i < maxLen) && (i < dataArray.Length); i++)
                {
                    transData[i] = Convert.ToByte(dataArray[i].Substring(0, 2), 16);
                }

                return Math.Min(maxLen, dataArray.Length);
            }
            catch (Exception)
            {
                return 0;
                throw;
            }


        }
        private uint MakeCanId(uint id, int eff, int rtr, int err)//1:extend frame 0:standard frame
        {
            uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0);
            uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0);
            uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0);
            return id | ueff << 31 | urtr << 30 | uerr << 29;
        }

        /// <summary>
        /// 设置网络工作模式
        /// </summary>
        /// <param name="channel"></param>
        /// <returns></returns>
        private bool setNetMode(int channel)
        {
            string path = channel + "/work_mode";
            string value = 1 == 0 ? "1" : "0";
            return 1 == Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(value));
        }

        /// <summary>
        /// 设置本地端口
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="localport"></param>
        /// <returns></returns>
        private bool setLocalPort(int channel)
        {
            string path = channel + "/local_port";
            return 1 == Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(LocalPort.ToString()));
        }


        /// <summary>
        /// 设置远程地址
        /// </summary>
        /// <returns></returns>
        private bool setRemoteAddr(int channel)
        {
            string path = channel + "/ip";
            return 1 == Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(RemoteAddr));
        }

        /// <summary>
        /// 设置远程端口
        /// </summary>
        /// <returns></returns>
        private bool setRemotePort(int channel)
        {
            string path = channel + "/work_port";

            return 1 == Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(RemotePort.ToString()));
        }

        /// <summary>
        /// 设置CANFD标准
        /// </summary>
        /// <param name="canfd_standard"></param>
        /// <returns></returns>
        private bool setCANFDStandard(int channel)
        {
            string path = channel + "/canfd_standard";
            string value = standard.ToString();
            //char* pathCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(path).ToPointer();
            //char* valueCh = (char*)System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(value).ToPointer();
            return 1 == Method.ZCAN_SetValue(_device_handle, path, Encoding.ASCII.GetBytes(value));
        }

    }
    /// <summary>
    /// CAN设备类型
    /// </summary>
    public enum DeviceType
    {
        CANFDNET_100U_TCP = 55,
        CANFDNET_200U_TCP = 48,
        CANFDNET_400U_TCP = 52,
        CANFDNET_800U_TCP = 57,
    }

    public enum NetMode
    {
        服务器 = 0,
        客户端 = 1
    }

    public enum Standard
    {
        CANFD_ISO = 0,
        CANFD_BOSCH = 1
    }

    /// <summary>
    /// 波特率
    /// </summary>
    public enum Abit
    {

    }

    /// <summary>
    /// 帧类型
    /// </summary>
    public enum Frametype
    {
        标准帧 = 0,
        扩展帧 = 1
    }

    /// <summary>
    /// 协议
    /// </summary>
    public enum Protocol
    {
        CAN = 0,
        CANFD = 1,
    }

    public enum SendType
    {
        正常发送 = 0,
        单次发送 = 1,
        自发自收 = 2,
        单次自发自收 = 3
    }
}
